package com.zqweb.gulimall.essearch.thread;

import com.zaxxer.hikari.util.UtilityElf;

import java.util.concurrent.*;

public class ThreadTest1 {
    // 共用的固定线程池，线程数量为15
    public static ExecutorService threadPool = Executors.newFixedThreadPool(15);
    /*
       Thread线程的4种创建方式：
        1）方式一：继承Thread类
        2）方式二：实现Runnable接口
        3）方式三：实现Callable接口
        4）方式四：使用线程池

        4种创建方式的比较：
          方式1、方式2都不能获取线程执行的返回结果，方式3可以获取（阻塞式获取），但是方式1、2、3都无法控制线程资源
          方式4可以控制线程资源，保证所有业务任务，都从线程池取出相应的线程（有就直接获取，没有就阻塞等待），实现了
          线程资源的共用和可控性，性能更加稳定
        业务使用情况：所有的多线程异步任务都会交给线程池，让线程池来分配线程资源去执行，缺点是：异步任务间调用存在依赖性
     */
    /*
        线程池的7大参数：
            参数1：池中的core线程数，线程池创建完毕就初始化完毕、准备就绪，生命周期与ThreadPool相同
            是一直存在的，专门提供给业务任务调度执行的
            参数2：池中的最大线程数，即池的容量
            参数3：对于非core线程的其他已创建的线程，如果处于空闲状态的时间超过了keepAliveTime，则
            会被释放、回收线程资源
            参数4：对应keepAliveTime的时间单位
            参数5：线程池的阻塞队列，当业务任务请求量超过core线程数，无法分配core线程处理业务任务请求，
            这些请求便会存放到阻塞队列中
            参数6：线程工厂ThreadFactory，用于制定线程的新建规则
            参数7：如果业务任务请求量过大，超过了core线程数，其余业务任务请求存于阻塞队列中，阻塞队列
            放满，此时新建Thread，但是线程池容量占满，还是无法继续分配线程处理业务任务请求，则ThreadPool
            会采取相应的拒绝策略，拒绝这些任务请求的调度执行

        线程池的拒绝策略：
         1）DiscardOldestPolicy：抛弃最之前处理的请求
         2）DiscardPolicy：抛弃当前处理的请求，不报异常
         3）AbortPolicy：抛弃当前处理的请求，报异常
         4）CallerRunsPolicy：当前处理的请求，不开辟线程（线程本来就不够），不抛弃而是直接同步调用对应的run方法即可

        线程池的分类说明：
            1）fixedThreadPool：固定线程数的线程池，池中每个线程都是core线程，无法回收
            2）cachedThreadPool：core线程数为0，所有线程都可进行回收
            3）singleThreadPool：单例/单线程的线程池，每次只能调度执行1个业务任务请求，其余请求无法执行，只能放到任务队列中，挨个执行
            4）scheduledThreadPool：定时任务调度型线程池

        线程池参数相关的面试题：
            1个线程池，core = 7，max = 20，queue： 50  请问：100个并发请求进行是如何分配线程的？
            7个core线程立即分配调度执行请求，然后放50个请求到queue中，请求量还是过大，新建并分配max中
            剩余的13个线程，还剩30个请求一定得不到处理，只能采取相应的拒绝策略，将其拒绝
     */
    static {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(7,
                20, 15, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),// 默认大小为Integer.MAX_VALUE(内存不够分配)
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());// 线程池的拒绝策略
        // 固定线程数的线程池，池中每个线程都是core线程，无法回收
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(15);
        // core线程数为0，所有线程都可进行回收
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 单例/单线程的线程池，每次只能调度执行1个业务任务请求，其余请求无法执行，只能放到任务队列中，挨个执行
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        // 定时任务调度型(周期性)线程池
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(7);
    }


    public static void main(String[] args) {
        System.out.println("main方法开始执行...");
        // 方式一：
        /*Demo1 demo1 = new Demo1();
        demo1.setName("线程1");
        demo1.start();*/
        // 方式二：
//        new Thread(new Demo2(),"线程2").start();
        // 方式三：（FutureTask本质还是实现了Runnable接口）
        /*FutureTask<String> futureTask = new FutureTask<>(new Demo3());
        new Thread(futureTask,"线程3").start();
        try {
            String res = futureTask.get();// 阻塞式等待获取线程执行结果
            System.out.printf("方式三造的线程执行结果为：%s\n",res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }*/
        // 方式四：
        threadPool.execute(() -> {
            String name = Thread.currentThread().getName();
            long id = Thread.currentThread().getId();
            System.out.println("name = " + name + ",id = " + id + "的分线程开始执行业务任务了...");
        });
        threadPool.shutdown();// 关闭当前线程池资源的调用，不然一直阻塞着占据资源

        System.out.println("main方法执行结束...");
    }

    // 方式一：
    public static class Demo1 extends Thread{
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            long id = Thread.currentThread().getId();
            System.out.println("name = " + name + ",id = " + id + "的分线程开始执行业务任务了...");
        }
    }
    // 方式二：
    public static class Demo2 implements Runnable{
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            long id = Thread.currentThread().getId();
            System.out.println("name = " + name + ",id = " + id + "的分线程开始执行业务任务了...");
        }
    }
    // 方式三：
    public static class Demo3 implements Callable<String>{
        @Override
        public String call() throws Exception {
            String name = Thread.currentThread().getName();
            long id = Thread.currentThread().getId();
            System.out.println("name = " + name + ",id = " + id + "的分线程开始执行业务任务了...");

            return "方式3成功新建线程！";
        }
    }

}
